package hsintiao.com.xintiaoAPP.service.impl;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import hsintiao.com.xintiaoAPP.config.AlipayConfig;
import hsintiao.com.xintiaoAPP.entity.Goods;
import hsintiao.com.xintiaoAPP.entity.PayBill;
import hsintiao.com.xintiaoAPP.entity.User;
import hsintiao.com.xintiaoAPP.entity.alipaybill.AliPayBill;
import hsintiao.com.xintiaoAPP.entity.alipaybill.AlipayTotalBill;
import hsintiao.com.xintiaoAPP.entity.alipaybill.DuplicateOrders;
import hsintiao.com.xintiaoAPP.entity.alipaybill.Reconciliation;
import hsintiao.com.xintiaoAPP.mapper.GoodsMapper;
import hsintiao.com.xintiaoAPP.mapper.PayBillMapper;
import hsintiao.com.xintiaoAPP.mapper.UserMapper;
import hsintiao.com.xintiaoAPP.service.PayBillService;
import hsintiao.com.xintiaoAPP.util.EmailUtil;
import hsintiao.com.xintiaoAPP.util.PoiUtil;
import hsintiao.com.xintiaoAPP.util.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class PayBillServiceImpl implements PayBillService {
    @Autowired
    private GoodsMapper goodsMapper;
    @Autowired
    private PayBillMapper payBillMapper;
    @Autowired
    private UserMapper userMapper;

    String filePath = "E:\\bill\\";
    //String filePath = "/yft/myProject/xtyl/paybill/";

    /*
    * 创建订单
    * */
    @Override
    public PayBill creatBill(Long goodsId, Long userID) {
        //获取商品信息
        Goods goodsById = goodsMapper.getGoodsById(goodsId);
        PayBill payBill = new PayBill();
        //设置商品名
        String itemName = goodsById.getItemName();
        payBill.setItemName(itemName);
        //设置订单金额(商品价格乘以数量)
        Long price = Long.valueOf((long) (goodsById.getPrice() * 1));
        payBill.setTotal_amount(price);
        //订单创建时间
        payBill.setGmtCreat(new Date());
        //创建订单号(时间戳+手机号+6位随机数)
        String source = "0123456789";
        Random r = new Random();
        StringBuilder rs = new StringBuilder();
        for (int j = 0; j < 6; j++) {
            rs.append(source.charAt(r.nextInt(10)));
        }
        long  timeNew =  System.currentTimeMillis();
        User userByID = userMapper.getUserByID(userID);
        String no= timeNew+userByID.getPhone()+rs;
        payBill.setOut_trade_no(no);
        System.out.println("out_trade_no="+no);

        payBill.setUserId(userID);
        return payBill;
    }

    /*
    * 保存订单
    * */
    @Override
    @Transactional
    //@Async("asyncServiceExecutor")
    public Boolean insertBill(PayBill payBill) {
        System.out.println(payBill);
        try {
            //根据订单号查询在保存订单操作前是否有重复的订单
            String out_trade_no = payBill.getOut_trade_no();
            List<PayBill> payBillsByOuttradeno = payBillMapper.selecByOuttradeno(out_trade_no);
            //存在重复订单，删除重复订单
            if (payBillsByOuttradeno.size() >0 || !payBillsByOuttradeno.isEmpty() || payBillsByOuttradeno != null){
                this.checkBillByInsert(payBillsByOuttradeno,payBill);
            }
            //保存订单
            payBillMapper.insert(payBill);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("保存订单失败"+e.getMessage());
        }
    }
    //删除重复订单
    private void checkBillByInsert(List<PayBill> payBillsByOuttradeno, PayBill payBill) {
        for (PayBill bill:payBillsByOuttradeno){
            if(payBill.getOut_trade_no().equals(bill.getOut_trade_no())){
                payBillMapper.deleteByID(bill.getId());
            }
        }

    }

    /*
    * 根据文件路径获取支付宝订单文件进行对账
    * */
    @Override
    public Boolean checkPayBill(String connet) {
        System.out.println(connet);
        //文件根据；符号分隔为多个文件
        String[] filePath = new String[2];
        String[] filePath1 = connet.split(";");
        //用于比对两个文件哪个属于汇总文件哪个属于明细文件，再赋值给对应的字符串数组
        String[] split1 = filePath1[0].split("\\|");
        System.out.println("汇总:"+filePath1[0]);
        if(split1[1].equals("#支付宝业务汇总查询")){
            System.out.println("是汇总");
            System.out.println(split1[1]);
            filePath[1]=filePath1[0];//设置汇总
            System.out.println(filePath1[0]);
            filePath[0] = filePath1[1];//设置明细
            System.out.println(filePath1[1]);
        }else {
            System.out.println("不是汇总");
            System.out.println(split1[1]);
            filePath[1] = filePath1[1];//设置汇总
            System.out.println(filePath1[1]);
            filePath[0] =filePath1[0];//设置明细
            System.out.println(filePath1[0]);
        }

        System.out.println("明细="+filePath[0]);
        System.out.println("汇总="+filePath[1]);
        //明细文件根据|分隔每一行数据
        String[] line = filePath[0].split("\\|");
        //获取每个订单的明细账单数据--从6开始因为读取的字符串中分隔后有一行为空，再除去固定的5行，剩下的都是订单行数据
        List<AliPayBill> aliPayBills = new ArrayList();
        for (int i=6;i<line.length-4;i++){
            //将每行数据根据，分隔每一个元素
            String[] title = line[i].split(",");
            //创建一个支付宝账单订单明细对象，存放支付宝中订单的数据
            AliPayBill aliPayBill = new AliPayBill();
            Reconciliation reconciliation = new Reconciliation();
            //获取相关参数
            for(int a=0;a<title.length;a++){
                reconciliation.setAlipayTradeNo(title[0].trim());
                aliPayBill.setAlipay_trade_no(title[0].trim());
                reconciliation.setOutTradeNo(title[1].trim());
                aliPayBill.setOut_trade_no(title[1].trim());
                aliPayBill.setTrade_type(title[2].trim());
                aliPayBill.setProject(title[3].trim());
                aliPayBill.setGmtCreat(title[4].trim());
                aliPayBill.setGmtPayment(title[5].trim());
                reconciliation.setAlipayAmount(Double.valueOf(title[11].trim()));
                aliPayBill.setTotal_amount(title[11].trim());
                reconciliation.setMerchantsAmount(Double.valueOf(title[12].trim()));
                aliPayBill.setReceipt_amount(title[12].trim());
                aliPayBill.setRemarks(title[24].trim());
            }
            System.out.println("对账账单单个订单="+reconciliation);
            System.out.println("明细对象="+aliPayBill);
            aliPayBills.add(aliPayBill);
            System.out.println("每行订单数据="+line[i]);
        }
        //汇总文件根据|分隔每一行数据
        String[] line2 = filePath[1].split("\\|");
        //同理，从7开始因为分隔后有一个空元素
        String[] title2 = line2[7].split(",");
        System.out.println("订单总数="+title2[2].trim()+",合计金额="+title2[4].trim()+",实际商家收款金额="+title2[5].trim()+",实收净额="+title2[11].trim());
        AlipayTotalBill alipayTotalBill = new AlipayTotalBill();
        alipayTotalBill.setAlipayOrders(Long.valueOf(title2[2].trim()));
        alipayTotalBill.setAlipayTotaoAmount(Double.valueOf(title2[4].trim()));
        alipayTotalBill.setReceiptAmount(Double.valueOf(title2[5].trim()));
        String date = line2[3];
        //获取汇总中的时间-用来查询和汇总中时间对应的数据
        String[] time = date.split("\\[");
        String[] time1 = time[1].split("]");
        String start=time1[0];
        String[] time2 = time[2].split("]");
        String end=time2[0];
        SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:ss:mm");
        Date start1 = null;
        Date end1 = null;
        try {
            start1 = format.parse(start);
            end1 = format.parse(end);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        //检查双方订单的总金额是否相等
        boolean checkTotalAmount = this.checkTotalAmount(alipayTotalBill,start1,end1);
        //判断汇总和重复订单造成的差价，都不满足就是有订单问题(双方账单中存在不重复且在另一方不存在的订单)
        if(checkTotalAmount){
            return true;
        }else {
            //获取双方存在但在另一方不存在的订单对账
            this.getNoDuplicateOrders(aliPayBills,start1,end1);
            return false;
        }
    }

    private void getNoDuplicateOrders(List<AliPayBill> aliPayBills, Date start, Date end) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        String start1 = format.format(start);
        String end1 = format.format(end);
        //查询数据库指定日期的订单(商户订单号，支付宝订单号)
        RedisUtils redisUtils = RedisUtils.INSTANCE;
        List<PayBill> payBills = payBillMapper.selectByDate(start,end);
        String key1="myPaybill";//数据库账单
        String key2="alipayPaybill";//支付宝账单
        String[] files = new String[2];
        //商户
        for (PayBill payBill:
                payBills) {
            String out_trade_no = payBill.getOut_trade_no();
            redisUtils.sadd(key1,out_trade_no);
        }
        //支付宝
        for (AliPayBill payBill:
                aliPayBills) {
            String out_trade_no = payBill.getOut_trade_no();
            redisUtils.sadd(key2,out_trade_no);
        }
        try {
            Set<String> alipay = redisUtils.sdiff(key2, key1);//支付宝有而数据库没有的
            List<AliPayBill> aliPayBillList = new ArrayList<>();
            for (String s:alipay) {
                for (AliPayBill ali :aliPayBills) {
                    if(s.equals(ali.getOut_trade_no())){
                        aliPayBillList.add(ali);
                    }
                }
            }
            PoiUtil.creatExcel(aliPayBillList,"支付宝有但数据库没有，表示在保存订单时失败，重新写入即可",
                    "支付宝存在订单",AliPayBill.class,start1+"-支付宝方订单.xls",filePath);
            System.out.println("支付宝有而数据库没有的"+alipay);
            if(alipay != null && !alipay.isEmpty()){
                files[0] = start1+"-支付宝方订单.xls";
                System.out.println("支付宝有但数据库没有，表示在保存订单时失败，重新写入即可");
                //查询支付宝有数据库没有的订单信息，重新保存到数据库
                for (String out_trade_no : alipay) {
                    this.queryBill(out_trade_no);
                }
            }
            //-------------------------------------
            Set<String> my = redisUtils.sdiff(key1, key2);
            if(my != null && my.size()>0){
                List<PayBill> mys = payBillMapper.selectBatch(my);
                PoiUtil.creatExcel(mys,"数据库有但支付宝没有，表示也许是订单的时间差问题导致当日支付宝账单没有改订单，删除，等待第二日对账时重新写入即可","数据库问题订单",
                        PayBill.class,start1+"-商户方订单.xls",filePath);
                System.out.println("数据库有而支付宝没有的"+my);
                if(my != null && !my.isEmpty()){
                    files[1] = start1+"-商户方订单.xls";
                    System.out.println("数据库有但支付宝没有，表示也许是订单的时间差问题导致当日支付宝账单没有改订单，删除，等待第二日对账时重新写入即可");
                    for (String out_trade_no :my) {
                        payBillMapper.deleteByOuttradeno(out_trade_no);
                        //将删除订单信息备份，通知人工查看
                    }
                }
            }
            try {
                String message = "双方账单都存在问题，导致总金额差匹配不上，删除重复订单后仍然出现问题" +
                        "列出双方的问题订单";
                sendMail(start,message,files);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                if(files[0]!= null && files[0].trim().length()>0){
                    File file = new File(filePath + files[0]);
                    file.delete();
                }
                if(files[1]!= null && files[1].trim().length()>0){
                    File file = new File(filePath + files[1]);
                    file.delete();
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            redisUtils.del(key1);
            redisUtils.del(key2);
        }
    }
    //查询支付宝中是否存在某个订单
    public String queryBill(String out_trade_no) {
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl,AlipayConfig.app_id,
                AlipayConfig.merchant_private_key,"json","UTF-8",
                AlipayConfig.alipay_public_key,"RSA2");
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        request.setBizContent("{" +
                "\"out_trade_no\":\""+out_trade_no+"\"" +
                "  }");
        AlipayTradeQueryResponse response = null;
        try {
            response = alipayClient.execute(request);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        String body = response.getBody();
        String subCode = response.getSubCode();
        System.out.println("subCode:"+subCode);
        if("ACQ.SYSTEM_ERROR".equals(subCode)){
            System.out.println("系统错误");
            return subCode;
        }else if("ACQ.INVALID_PARAMETER".equals(subCode)){
            System.out.println("参数无效");
            return subCode;
        }else if("ACQ.TRADE_NOT_EXIST".equals(subCode)){
            System.out.println("查询订单不存在");
            return subCode;
        }else {
            if(response.isSuccess()){
                PayBill bill = new PayBill();
                bill.setOut_trade_no(response.getOutTradeNo());
                Long userID = getUserID(response.getOutTradeNo());
                bill.setUserId(userID);
                bill.setTrade_no(response.getTradeNo());
                bill.setItemName(response.getSubject());
                String amount = response.getTotalAmount();
                Long totalAmount = (long)(Double.valueOf(amount)*100);
                bill.setTotal_amount(totalAmount);
                bill.setReceipt_amount(totalAmount);
                bill.setStatus(1);
                bill.setTrade_status("trade_success");
                bill.setGmtCreat(response.getSendPayDate());
                bill.setGmtPayment(response.getSendPayDate());
                bill.setBuyer_logon_id(response.getBuyerLogonId());
                bill.setBuyer_id(response.getBuyerUserId());
                payBillMapper.insert(bill);
                System.out.println("重新保存订单成功");
                return "true";
            } else {
                System.out.println("重新保存订单失败");
                return "false";
            }
        }
    }

    /*
    1-总金额对比，如果总金额匹配不上，检查是否有重复订单，重复订单的总金额是否能补齐差价
    删除重复订单保留至少一条订单即可
    2-重复订单差价补不齐，或者没有重复的订单，要么是数据库有的订单但支付宝中没有，要么是支付宝中有的数据库没有
    2-1数据库有，但支付宝中没有的，调用查询接口，循环查询订单是否在支付宝中存在，解决时间差问题的订单
    2-2数据库没有但支付宝中有的，在保存订单时保存失败，重新保存即可，保存完毕后重新对比总金额
    2-3数据有但支付宝没有，同时，支付宝有，数据库没有，redis SDIFF查出不同的差异订单保存交给人工处理
    */
    //根据支付宝账单汇总对比指定日期内的双方账单的总额和是否有重复订单造成的金额差(重复订单造成的金额差能否补齐差价)
    private boolean checkTotalAmount(AlipayTotalBill alipayTotalBill, Date start,Date end) {
        long time = end.getTime();
        time = time-1000;
        end.setTime(time);
        Long totalAmountByDate = payBillMapper.getTotalAmountByDate(start, end);
        if(totalAmountByDate == null){
            totalAmountByDate = 0L;
        }
        System.out.println("总金额="+totalAmountByDate);
        Double alipayTotaoAmount1 = alipayTotalBill.getAlipayTotaoAmount();
        Long alipayTotaoAmount = Long.valueOf((long) (alipayTotaoAmount1 * 100));
        System.out.println("支付总金额="+alipayTotaoAmount);
        if(alipayTotaoAmount.equals(totalAmountByDate) || alipayTotaoAmount == totalAmountByDate){
            System.out.println("两个账单金额相同");
            this.allOKBillMail(start);
            return true;
        }else {
            System.out.println("两个账单金额不相同");
            //检查是否有重复订单造成商户总金额不匹配
            List<DuplicateOrders> orders = payBillMapper.checkDuplicateOrders(start,end);
            Double price = 0.0;
            if(orders != null && orders.size()>0){
                if(orders.size() >0 || !orders.isEmpty()){
                    for (DuplicateOrders d :
                            orders) {
                        price = price+d.getTotal_amount()*(d.getCount()-1);
                    }
                }
            }
            Long price1 = Long.valueOf((long) (price*1));
            System.out.println("差价="+price1);
            if(alipayTotaoAmount.equals(totalAmountByDate-price1)){
                System.out.println("重复订单造成差价");
                //删除重复订单即可
                try {
                    for (DuplicateOrders dp :orders) {
                        String out_trade_no = dp.getOut_trade_no();
                        payBillMapper.deleteByDuplicateOuttradeno(out_trade_no);
                    }
                }catch (Exception e){
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                }
                String fileName = "重复订单.xls";
                try {
                    //生成excle文件
                    PoiUtil.creatExcel(orders,"重复订单","重复订单",DuplicateOrders.class,
                            fileName,filePath);
                    String message = "重复订单造成差价，金额差匹配，删除了重复订单";
                    String[] files = new String[1];
                    files[0]=fileName;
                    //发送带附件的邮件
                    this.sendMail(start,message,files);
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    //删除本地的文件
                    File file = new File(filePath + fileName);
                    file.delete();
                }
                return true;//重复订单造成差价不相等，删掉重复的订单后双方账单总金额相等
            }else {
                System.out.println("有重复订单，造成的差价不足以修正总金额");
                for (DuplicateOrders dp :orders) {
                    String out_trade_no = dp.getOut_trade_no();
                    payBillMapper.deleteByDuplicateOuttradeno(out_trade_no);
                }
                /*
                有重复订单，但订单总金额不足以补上差价，或者没有重复订单，订单保存时出现错误
                造成支付宝账单存在的订单数据库没有，这种情况就重新保存订单到数据库
                如果是订单都存在且不存在重复订单问题，但有些订单的金额不匹配
                此时将这些订单获取到数据信息保存交给人工处理
                */
                return false;//删除重复订单后总金额仍不相等
            }
        }
    }

    private String path = "E:\\bill\\";
    //private String path = "/yft/myProject/xtyl/paybill/";
    @Autowired
    private JavaMailSender mailSender;
    //没有问题时发送的账单邮件
    public void allOKBillMail(Date date){
        //date.setTime(date.getTime()-1000*60*60*24);
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        String time = format.format(date);
        System.out.println("日期="+time);
        System.out.println("日期="+date);
        MimeMessage message = mailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom("931409313@qq.com");
            helper.setTo("1730592409@qq.com");
            helper.setSubject(time+"的账单");
            helper.setText("该日支付宝账单没有问题，携带该日支付宝账单文件");
            FileSystemResource file = new FileSystemResource(path+time+".zip");
            helper.addAttachment(time+".zip",file);
        } catch (MessagingException e) {
            e.printStackTrace();
        }
        mailSender.send(message);
    }

    private void sendMail(Date date,String message,String[] fileName){
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        String time = format.format(date);
        System.out.println("日期="+time);
        System.out.println("日期="+date);
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
            helper.setFrom("931409313@qq.com");
            helper.setTo("1730592409@qq.com");
            helper.setSubject(time+"的账单");
            helper.setText(message);
            for (int i=0;i<fileName.length;i++){
                if(fileName[i]!=null && fileName[i].trim().length()>0 && !fileName[i].isEmpty()){
                    File file = new File(path + fileName[i]);
                    //FileSystemResource file = new FileSystemResource(path+fileName[i]);
                    helper.addAttachment(fileName[i],file);
                }
            }
        } catch (MessagingException e) {
            e.printStackTrace();
        }
        mailSender.send(mimeMessage);
    }


    //从商户订单号中获取手机号判断属于哪一个用户
    public Long getUserID(String out_trade_no){
        System.out.println("out_trade_no="+out_trade_no);
        String substring = out_trade_no.substring(13,24);
        System.out.println("substring="+substring);
        User byUsername = userMapper.findByUsername(substring);
        return byUsername.getId();
    }
}
